package dataImport.action;

import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import dataImport.action.abstracts.AbstractTempEntityReader;
import dataImport.action.helper.UnityGuardian;
import dataImport.action.manager.EntityReaderManager;
import dataImport.action.manager.storage.EntityReaderStorageKey;
import dataImport.exception.DuplicateAttributeException;
import dataImport.exception.DuplicateDeclarationException;
import dataImport.exception.MissingAttributeException;
import dataImport.exception.MissingDeclarationException;
import dataImport.exception.MissingReader;
import dataImport.exception.NoEntityRelatedWithException;
import dataImport.exception.UniquenessRestrictionViolatedException;
import dataImport.model.Configuration;
import dataImport.model.ConfigurationTemp;
import dataImport.model.CustomAction;
import dataImport.model.Object;
import dataImport.model.Place;
import dataImport.model.State;
import dataImport.model.StaticConfiguration;
import dataImport.model.abstracts.AbstractEntity;


public class ConfigurationReader extends AbstractTempEntityReader {

	private static final long serialVersionUID = 1882448166070850033L;

	private static final String ENTITY_NAME = "configuration";

	private static final String ATTRIBUTE_VALUE = "value";

	private UnityGuardian staticConfigurationEntityId;

	public ConfigurationReader() {
		super();
		this.staticConfigurationEntityId = new UnityGuardian();
	}

	@Override
	protected void checkRequiredReaders(final EntityReaderManager entityReaderManager) throws MissingReader {
		entityReaderManager.contains(EntityReaderStorageKey.OBJECT);
		entityReaderManager.contains(EntityReaderStorageKey.PLACE);
		entityReaderManager.contains(EntityReaderStorageKey.STATE);
		entityReaderManager.contains(EntityReaderStorageKey.CUSTOM_ACTION);
		entityReaderManager.getCustomActionReader().isFixed();
	}

	@Override
	protected AbstractEntity createEntity(final Element element) {
		final String name = element.getNodeName();
		final String value = element.getAttribute(ATTRIBUTE_VALUE);
		final Element parentNode = (Element) element.getParentNode();
		final String parentNodeName = parentNode.getNodeName();

		this.verifyRestrictions(parentNodeName, name, value);

		return new ConfigurationTemp(StaticConfiguration.get(name), value);
	}

	@Override
	protected void fixEntities(HashMap<String, AbstractEntity> newEntities, final EntityReaderManager entityReaderManager) throws MissingReader {
		final ObjectReader objectReader = entityReaderManager.getObjectReader();
		final PlaceReader placeReader = entityReaderManager.getPlaceReader();
		final StateReader stateReader = entityReaderManager.getStateReader();
		final CustomActionReader customActionReader = entityReaderManager.getCustomActionReader();

		for (final AbstractEntity abstractEntity : this.abstractEntities.values()) {
			final ConfigurationTemp configurationTemp = (ConfigurationTemp) abstractEntity;

			final StaticConfiguration staticConfiguration = configurationTemp.getStaticConfiguration();
			final String entityId = configurationTemp.getEntityId();

			switch (staticConfiguration) {
				case INITIALACTION:
					final CustomAction customAction = (CustomAction) customActionReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(staticConfiguration.getName(), entityId), new Configuration(staticConfiguration, customAction));
					break;
				case INITIALOBJECT:
					final Object object = (Object) objectReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(staticConfiguration.getName(), entityId), new Configuration(staticConfiguration, object));
					break;
				case INITIALSTATE:
					final State state = (State) stateReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(staticConfiguration.getName(), entityId), new Configuration(staticConfiguration, state));
					break;
				case INITIALPLACE:
					final Place place = (Place) placeReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(staticConfiguration.getName(), entityId), new Configuration(staticConfiguration, place));
					break;
				default:
					break;
			}
		}

	}

	@Override
	public void read(final Document document) {
		final NodeList nodeList = document.getElementsByTagName(ConfigurationReader.ENTITY_NAME);

		if (nodeList.getLength() == 0) {
			throw new MissingDeclarationException(ConfigurationReader.ENTITY_NAME);
		}

		if (nodeList.getLength() > 1) {
			throw new DuplicateDeclarationException(ConfigurationReader.ENTITY_NAME);
		}

		for (StaticConfiguration staticConfiguration : StaticConfiguration.values()) {
			super.read(staticConfiguration.getName(), document);
		}
	}

	private void verifyRestrictions(final String configurationName, final String staticConfigurationName, final String entityId) {
		if (entityId.isEmpty()) {
			throw new MissingAttributeException(ATTRIBUTE_VALUE);
		}

		try {
			this.staticConfigurationEntityId.ensureUniqueness(staticConfigurationName, entityId);
		}
		catch (final UniquenessRestrictionViolatedException e) {
			throw new DuplicateAttributeException("configuration", staticConfigurationName);
		}

		if (!configurationName.equals(ConfigurationReader.ENTITY_NAME)) {
			throw new NoEntityRelatedWithException(staticConfigurationName, ConfigurationReader.ENTITY_NAME);
		}
	}
}
